(:
  module namespace explanation:
  -------------------------------------
		the module namespace consists of the base project uri (see .config/sausalito.xml) plus the module
		handler name (that is equal to the .xq file name)
:)
module namespace tc = "http://twittercharts.uzh.ch/default";

import module namespace um = "http://twittercharts.uzh.ch/user-management";
import module namespace cookie = "http://www.28msec.com/modules/http/cookie";
import module namespace http = "http://www.28msec.com/modules/http";
import module namespace http-client = "http://expath.org/ns/http-client";
import module namespace xqddf = "http://www.zorba-xquery.com/modules/xqddf";

declare ordered collection tc:tweets as node()*;
declare ordered collection tc:rates as node()*;
declare variable $tc:tweets := xs:QName("tc:tweets");
declare variable $tc:rates := xs:QName("tc:rates");

(:
 : Gets the Tweets of all registered users, as stored in um:users, sorts them
 : by rating and outputs them.
 :)
declare sequential function tc:global-ranking()
{
    (: check if user is logged in :)
    declare $user-id := if (cookie:validate-session())
        then cookie:get-session-data()/id/text()
        else ();
        
    (: collect Tweets of all registered users :)
    declare $entries := for $user in xqddf:collection($um:users)
        return
            http-client:read(
            fn:concat(
                "http://api.twitter.com/1/statuses/user_timeline.xml?count=200&amp;screen_name=",
                $user/screen_name/text()
            )
        )//status;
    
    <html>
        <head>
            <title>Global Twitter Charts</title>
            <script type="text/javascript" src="/jquery-1.4.3.js"></script>
            <script type="text/javascript" src="/scripts.js"></script>
            <link rel="stylesheet" type="text/css" href="/style.css" />
        </head>
        <body>
            <div class="main">
            <div class="center">
                <h1>The Twitter Charts</h1>
                <a href="/index.html">Back to Home Page</a>
                {
                    if (cookie:validate-session())
                    then (
                        " | ", <a href="show-profile">Your Profile</a>,
                        " | ", <a href="/authorization/log-out">Log-out</a>
                    )
                    else
                        ""
                }
            </div>
            {
                (: output Tweets ordered by rating :)
                for $entry in $entries
                let
                    $points := tc:get-points($entry/id/text()) cast as xs:integer,
                    $rt-count := if ($entry/retweet_count eq <retweet_count/>)
                        then 0
                        else $entry/retweet_count/text() cast as xs:integer,
                    $rating := $points + $rt-count
                order by
                    $rating descending
                return
                    tc:display-tweet($entry,$user-id)
            }
            </div>
        </body>
        <div id="log-in">
            <a href="/authorization/log-in"><img src="/sign-in-with-twitter.png" border="0" /></a>
        </div>
        <div id="overlay"></div>
        <div id="loader"></div>
    </html>
};

(:
 : Shows the rated Tweets of a single user.
 :)
declare sequential function tc:show-profile()
{
    (: check if user is logged in :)
    declare $user-id := if (cookie:validate-session())
        then cookie:get-session-data()/id/text()
        else ();
    
    (:
     : If no Twitter username is provided as GET parameter, the profile of the
     : currently logged in user will be shown.
     :)
    declare $screen-name := if (http:get-parameters("screen_name"))
        then http:get-parameters("screen_name")
        else (
            if (cookie:validate-session())
            then
                cookie:get-session-data()/screen_name/text()
            else
                http:send-redirect("/authorization/log-in")
        );
    
    declare $profile := http-client:read(    
        fn:concat("http://twitter.com/users/show/", $screen-name, ".xml")
    );
    declare $profile_image_url := $profile/user/profile_image_url;   
    declare $bg_url := $profile/user/profile_background_image_url;
    declare $bg_color := $profile/user/profile_background_color;
    declare $bg_tile := $profile/user/profile_background_tile;
    declare $bg_use := $profile/user/profile_use_background_image;
    
    <html>
        <head>
            <title>Profil von {$screen-name}</title>
            <script type="text/javascript" src="/jquery-1.4.3.js"></script>
            <script type="text/javascript" src="/scripts.js"></script>
            <link rel="stylesheet" type="text/css" href="/style.css" />
            <style>
                body &#x7B;
                    background-color: {$bg_color/text()};
                    {
                        if ($bg_use/text() eq "true")
                        then (
                            "background-image: url(",$bg_url/text(),");",
                            "background-repeat: ",if ($bg_tile/text() eq "false") then "repeat-x;" else "no-repeat;"
                        ) else (
                        )
                    }
                &#x7D;
            </style>
        </head>
        <body>
            <div class="main">
                <div class="center">
                    <h1>Profil von <a href="http://twitter.com/{$screen-name}">@{$screen-name}</a></h1>
                    <img src="{$profile_image_url}"></img><br /><br />
                    <a href="http://twitter-charts.my28msec.com/index.html">Watch a different Profile</a>
                    {
                        if (cookie:validate-session())
                        then (
                            " | ", <a href="show-profile">Your Profile</a>,
                            " | ", <a href="/authorization/log-out">Log-out</a>
                        )
                        else
                            ""
                    }
                    <br /><br />
                </div>
                {
                    tc:profile($user-id, $screen-name)
                }
            </div>
            <div id="log-in">
                <a href="/authorization/log-in"><img src="/sign-in-with-twitter.png" border="0" /></a>
            </div>
            <div id="overlay"></div>
            <div id="loader"></div>
        </body>
    </html>
};

(:
 : Gets the Tweets of a single user and orders them by rating.
 :)
declare function tc:profile ($user-id, $screen-name)
{
    for
        $tweet in http-client:read(
            fn:concat(
                "http://api.twitter.com/1/statuses/user_timeline.xml?count=200&amp;screen_name=",
                $screen-name
            )
        )//status
    let
        $points := tc:get-points($tweet/id/text()) cast as xs:integer,
        $rt-count := if ($tweet/retweet_count eq <retweet_count/>)
            then 0
            else $tweet/retweet_count/text() cast as xs:integer,
        $rating := $points + $rt-count
    order by
        $rating descending
    return
        tc:display-tweet($tweet,$user-id)
};

(:
 : Provides the HTML representation of a Tweet.
 :)
declare function tc:display-tweet($tweet as node(), $user-id)
{
    let
        $text := $tweet/text/text(),
        $tweet-id := $tweet/id/text(),
        $tweet-date := $tweet/created_at/text(),
        $screen-name := $tweet/user/screen_name/text(),
        $points := tc:get-points($tweet-id) cast as xs:integer,
        $rt-count := if ($tweet/retweet_count eq <retweet_count/>)
            then 0
            else $tweet/retweet_count/text() cast as xs:integer,
        $rating := $points + $rt-count,
        $operator := if ($points lt 0)
            then " - "
            else " + ",
        $pointsDisplay := if ($points lt 0)
            then -1 * $points
            else $points,
        
        (:
         : Gets an entry from the "rates" table in the DB in case the logged in
         : user has already rated this Tweet.
         :)
        $rated := xqddf:collection($tc:rates)[/tweet_id/text() eq $tweet-id][/user_id/text() eq $user-id]
        
    return
        <div class="tweet">
            <strong><a href="http://twitter.com/{$screen-name}">{$screen-name}</a>: </strong>{$text} ({
                fn:concat(fn:substring($tweet-date,5,6),
                ",",
                fn:substring($tweet-date,26))
            })<br />
            Rating: <span id="rating_{$tweet-id}">{$rt-count} {$operator} {$pointsDisplay} = {$rating}</span><br />
            <span id="links_{$tweet-id}">
            {
                (: Links for rating are only displayed if user is logged in :)
                if (cookie:validate-session() and fn:not($rated))
                then (
                    <a href="javascript:;" onclick="thumbsUp('{$tweet-id}','{$tweet/user/id}','{$user-id}',{$rt-count},{$points})"><img src="/thumbsup.png" border="0" /></a>,
                    <a href="javascript:;" onclick="thumbsDown('{$tweet-id}','{$tweet/user/id}','{$user-id}',{$rt-count},{$points})"><img src="/thumbsdown.png" border="0" /></a>
                )
                else (
                    (: Show "undo" link if user has already rated this Tweet. :)
                    if ($rated)
                    then(
                        <span>You rated <strong>{if (($rated/rating cast as xs:integer) lt 0) then "-1" else "+1"}</strong> </span>,
                        <a href="javascript:;" onclick="undo('{$tweet-id}','{$tweet/user/id}','{$user-id}',{$rt-count},{$points})">[undo]</a>
                    )
                    else (
                    )
                )
            }
            </span>
        </div>,<br />
};

(:
 : Get the thumbs-up/down points of a Tweet from the DB.
 :)
declare function tc:get-points ($id as xs:string)
{
    let $tweet := xqddf:collection($tc:tweets)[/id/text() eq $id]
    return
        if ($tweet)
        then
            $tweet/points
        else
            "0"
};

(:
 : Changes the rating of the according tweet (specified by GET parameter "id")
 : in the DB or creates a new entry in case the Tweet has not yet been rated.
 : Returns "1" if successful, and "0" if the logged in user has already rated
 : the Tweet. 
 :)
declare sequential function tc:thumbs-up ()
{
    declare $tweet-id := http:get-parameters("id");
    declare $owning-user-id := http:get-parameters("owning");
    declare $rating-user-id := http:get-parameters("rating");
    declare $rated := xqddf:collection($tc:rates)[/tweet_id/text() eq $tweet-id][/user_id/text() eq $rating-user-id]; 
    declare $rating := 0;
    
    if ($rated)
    then (
    )
    else (
        if (xqddf:collection($tc:tweets)[/id/text() eq $tweet-id])
        then
            tc:update-tweet($tweet-id, +1)
        else
            tc:add-tweet($tweet-id, $owning-user-id, +1),
        
        tc:record-rating($tweet-id, $rating-user-id, +1)
    );
    
    if ($rated)
    then (
    )
    else (
        set $rating := 1
    );
    
    exit returning $rating;
};

(:
 : Changes the rating of the according tweet (specified by GET parameter "id")
 : in the DB or creates a new entry in case the Tweet has not yet been rated.
 : Returns "-1" if successful, and "0" if the logged in user has already rated
 : the Tweet. 
 :)
declare sequential function tc:thumbs-down ()
{
    declare $tweet-id := http:get-parameters("id");
    declare $owning-user-id := http:get-parameters("owning");
    declare $rating-user-id := http:get-parameters("rating");
    declare $rated := xqddf:collection($tc:rates)[/tweet_id/text() eq $tweet-id][/user_id/text() eq $rating-user-id];
    declare $rating := 0;
    
    if ($rated)
    then (
    )
    else (
        if (xqddf:collection($tc:tweets)[/id/text() eq $tweet-id])
        then
            tc:update-tweet($tweet-id, -1)
        else
            tc:add-tweet($tweet-id, $owning-user-id, -1),
        
        tc:record-rating($tweet-id, $rating-user-id, -1)
    );
    
    if ($rated)
    then (
    )
    else (
        set $rating := -1;
    );
    
    exit returning $rating;
};

(:
 : Updates the entry of a Tweet in the DB (specified by $id) according to the
 : given $points parameter.
 :)
declare updating function tc:update-tweet ($id as xs:string, $points as xs:integer)
{
    let $tweet := xqddf:collection($tc:tweets)[/id/text() eq $id]/points
    return
        replace value of node
            $tweet
        with
            $tweet/text() + $points
};

(:
 : Creates a new entry for a Tweet in the DB. Required parameters are Tweet ID,
 : the ID of the user owning the Tweet and the amount of thumbs-up/down points.
 :)
declare updating function tc:add-tweet ($id as xs:string, $user-id as xs:string, $points as xs:integer)
{
    xqddf:insert-nodes-first(
        $tc:tweets,
        <tweet>
            <id>{$id}</id>
            <user_id>{$user-id}</user_id>
            <points>{$points}</points>
        </tweet>
    )
};

(:
 : Determines the rating a user (specified by GET parameter "user") has given
 : to a Tweet (specified by GET parameter "id") and revokes it by updating the
 : Tweet's entry in the DB. Moreover, the user-Tweet relationship is deleted
 : from the "rates" table, so that the user can rate the Tweet again. Returns
 : the amount of points that has been revoked.
 :)
declare sequential function tc:undo ()
{
    declare $tweet-id := http:get-parameters("id");
    declare $user-id := http:get-parameters("user"); 
    declare $rating := xqddf:collection($tc:rates)[/tweet_id/text() eq $tweet-id][/user_id/text() eq $user-id];
    
    tc:update-tweet(
        $tweet-id,
        -1 * ($rating/rating/text() cast as xs:integer)
    );
    
    xqddf:delete-nodes(
        $tc:rates,
        $rating
    );
    
    exit returning $rating/rating/text();
};

(:
 : Creates an entry in the "rates" table, thus recording that a user (specified
 : by $user-id) has rated a Tweet (specified by $tweet-id) and cannot rate it
 : anymore. 
 :)
declare updating function tc:record-rating ($tweet-id as xs:string, $user-id as xs:string, $rating as xs:integer)
{
    xqddf:insert-nodes-first(
        $tc:rates,
        <rates>
            <tweet_id>{$tweet-id}</tweet_id>
            <user_id>{$user-id}</user_id>
            <rating>{$rating}</rating>
        </rates>
    )
};
